﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Web;
using SelArom.Net.Sitefinity.API.News.Data;
using SelArom.Net.Sitefinity.Helpers;
using Telerik.Cms.Engine;
using Telerik.News;
using Telerik.News.Configuration;
using Newtonsoft.Json;

namespace SelArom.Net.Sitefinity.API.News.Data
{
	/// <summary>
	/// Wrapper class for News Items (IContent)
	/// </summary>
	public class NewsDetails : IComparable
	{
		#region Constructors
		public NewsDetails() { }

		/// <summary>
		/// Initializes a new instance of the <see cref="NewsDetails"/> class from an existing News (IContent) item.
		/// </summary>
		/// <param name="sNews">The IContent news item from which to copy data.</param>
		/// <param name="InitContent">if set to <c>true</c>, initializes the full Content body</param>
		public NewsDetails(IContent sNews, bool InitContent)
		{
			Init(sNews, InitContent);
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="NewsDetails"/> class from an existing News (IContent item. DOES NOT initialize full Content body.
		/// </summary>
		/// <param name="sNews">The IContent news item from which to copy data.</param>
		public NewsDetails(IContent sNews)
		{
			Init(sNews, false);
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="NewsDetails"/> class from an existing News (IContent) item.
		/// </summary>
		/// <param name="sNews">The IContent news item from which to copy data.</param>
		/// <param name="InitContent">if set to <c>true</c>, initializes the full Content body</param>
		private void Init(IContent sNews, bool InitContent)
		{
			// copy IContent Items
			ID = sNews.ID.ToString();
			var title = sNews.GetMetaData("Name") ?? sNews.GetMetaData("Title");
			NewsTitle = title.ToString().Replace("\"", "'");
			Category = sNews.GetMetaData("Category").ToString();
			PublicationDate = DateTime.Parse(sNews.GetMetaData("Publication_Date").ToString());
			ExpirationDate = DateTime.Parse(sNews.GetMetaData("Expiration_Date").ToString());

			// copy Content (if requested) and summary
			var cnt = sNews.Content.ToString();
			Content = InitContent ? cnt : string.Empty;
			Summary = sNews.GetMetaData("Summary").ToString();

			// ensure url appkey is present, initialize full url
			var evUrl = ConfigurationManager.AppSettings[NewsManager.DefaultContentProvider + "Url"];
			var url = string.IsNullOrEmpty(evUrl) ? sNews.UrlWithExtension : string.Concat(evUrl, sNews.UrlWithExtension);
			Url = string.Concat("http://", HttpContext.Current.Request.Url.Host, url.ToLower());

			// optional Thumbnail
			var thumb = sNews.GetMetaData("Thumbnail");
			var thumbUrl = thumb == null  || string.IsNullOrEmpty(thumb.ToString()) ? "": thumb.ToString().Substring(1);
			Thumbnail = string.Concat("http://", HttpContext.Current.Request.Url.Host, thumbUrl);
		}
		#endregion

		#region "Properties"
		/// <summary>
		/// Gets or sets the News Item ID.
		/// </summary>
		/// <value>The news Item ID.</value>
		public string ID { get; set; }

		/// <summary>
		/// Gets or sets the news title.
		/// </summary>
		/// <value>The news title.</value>
		public string NewsTitle { get; set; }

		/// <summary>
		/// Gets or sets the news item publication date.
		/// </summary>
		/// <value>The publication date of the news item.</value>
		public DateTime PublicationDate { get; set; }
		/// <summary>
		/// Gets or sets the news item expiration date.
		/// </summary>
		/// <value>The expiration date of the news item.</value>

		public DateTime ExpirationDate { get; set; }
		/// <summary>
		/// Gets or sets the news item category.
		/// </summary>
		/// <value>The category of the news item.</value>
		public string Category { get; set; }

		/// <summary>
		/// Gets or sets the news item content (body).
		/// </summary>
		/// <value>The content (body) of the news item.</value>
		public string Content { get; set; }

		/// <summary>
		/// Gets or sets the news item summary.
		/// </summary>
		/// <value>The summary of the news item.</value>
		public string Summary { get; set; }

		/// <summary>
		/// Gets or sets the news item URL.
		/// </summary>
		/// <value>The URL for the news item.</value>
		public string Url { get; set; }

		/// <summary>
		/// Gets or sets the news item thumbnail.
		/// </summary>
		/// <value>The thumbnail of the thumbnail.</value>

		public string Thumbnail { get; set; }
		#endregion

		#region IComparable Members
		public int CompareTo(object obj)
		{
			var ev = obj as NewsDetails;
			if (ev == null)
				throw new ArgumentException("Argument is not the correct type");
			return PublicationDate.CompareTo(ev.PublicationDate);
		}
		#endregion
	}

	/// <summary>
	/// Collection of NewsDetails objects
	/// </summary>
	public class NewsList : List<NewsDetails> { }
}

namespace SelArom.Net.Sitefinity.API.News
{
	/// <summary>
	/// Class for retrieving data from the Default Data Provider for Sitefinity News Module.
	/// </summary>
	public class NewsAPI
	{
		protected NewsManager mgr;
		protected HttpServerUtility Server;
		private const string CACHE_LOCATION = "~/App_Data/API/news";

			/// <summary>
		/// Initializes a new instance of the <see cref="NewsAPI"/> class.
		/// </summary>
		public NewsAPI()
		{
			Server = HttpContext.Current.Server;
			var handler = (SectionHandler)ConfigurationManager.GetSection("telerik/news");
			mgr = new NewsManager(handler.DefaultProvider);
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="NewsAPI"/> class.
		/// </summary>
		/// <param name="ProviderName">Name of the provider.</param>
		public NewsAPI(string ProviderName)
		{
			mgr = new NewsManager(ProviderName);
		}

		#region APIMethods
		/// <summary>
		/// Clears the file cache
		/// </summary>
		public static void Clear()
		{
			CacheHelper.Clear(CACHE_LOCATION);
		}

		/// <summary>
		/// Retrieve all published news items
		/// </summary>
		/// <returns></returns>
		public NewsList LatestNews()
		{
			var published = new NewsList();

			// create CACHE_LOCATION file if required
			var path = string.Format("{0}/all.json", CACHE_LOCATION);
			var file = new FileInfo(Server.MapPath(path));
			var cache = CacheHelper.ReadFile(file);

			// check CACHE and refresh if necessary
			if (string.IsNullOrEmpty(cache))
			{
				var filters = new IMetaSearchInfo[2];
				filters[0] = new MetaSearchInfo(MetaValueTypes.DateTime, "Expiration_Date", DateTime.Now, SearchCondition.GreaterOrEqual);
				filters[1] = new MetaSearchInfo(MetaValueTypes.DateTime, "Publication_Date", DateTime.Now, SearchCondition.LessOrEqual);
				var newsList = mgr.Content.GetContent("Publication_Date DESC", filters);

				// only retrieve published items
				foreach (IContent item in newsList)
				{
					var news = new NewsDetails(item);
					if (item.Status == ContentStatus.Published) published.Add(news);
				}
				cache = JsonConvert.SerializeObject(published);
				CacheHelper.WriteFile(file.FullName, cache);
			}
			else
				// Cache is still valid
				published = JsonConvert.DeserializeObject<NewsList>(cache);

			return published;
		}

		/// <summary>
		/// Retrieves published news from the specified category
		/// </summary>
		/// <param name="Category">The category.</param>
		/// <returns></returns>
		public NewsList LatestNews(string Category)
		{
			var published = new NewsList();

			// create CACHE_LOCATION file if required
			var path = string.Format("{0}/{1}.json", CACHE_LOCATION, Category);
			var file = new FileInfo(Server.MapPath(path));
			var cache = CacheHelper.ReadFile(file);

			// check CACHE and refresh if necessary
			if (string.IsNullOrEmpty(cache))
			{
				var filters = new IMetaSearchInfo[3];
				filters[0] = new MetaSearchInfo(MetaValueTypes.DateTime, "Expiration_Date", DateTime.Now, SearchCondition.GreaterOrEqual);
				filters[1] = new MetaSearchInfo(MetaValueTypes.ShortText, "Category", Category);
				filters[2] = new MetaSearchInfo(MetaValueTypes.DateTime, "Publication_Date", DateTime.Now,
												SearchCondition.LessOrEqual);

				var newsList = mgr.Content.GetContent("Publication_Date DESC", filters);
				
				// only retrieve published items
				foreach (IContent item in newsList)
				{
					var news = new NewsDetails(item);
					if (item.Status == ContentStatus.Published) published.Add(news);
				}
				cache = JsonConvert.SerializeObject(published);
				CacheHelper.WriteFile(file.FullName, cache);
			}
			else
				// Cache is still valid
				published = JsonConvert.DeserializeObject<NewsList>(cache);

			return published;
		}

		/// <summary>
		/// Retrieves published news specified as "TopStory"
		/// </summary>
		/// <returns></returns>
		public NewsList FeaturedNews()
		{
			var published = new NewsList();

			// create CACHE_LOCATION file if required
			var path = string.Format("{0}/featured.json", CACHE_LOCATION);
			var file = new FileInfo(Server.MapPath(path));
			var cache = CacheHelper.ReadFile(file);

			// check CACHE and refresh if necessary
			if (string.IsNullOrEmpty(cache))
			{
				// TODO: Make "TopStory" Meta Key a configurable setting
				var filters = new IMetaSearchInfo[3];
				filters[0] = new MetaSearchInfo(MetaValueTypes.DateTime, "Expiration_Date", DateTime.Now, SearchCondition.GreaterOrEqual);
				filters[1] = new MetaSearchInfo(MetaValueTypes.Boolean, "TopStory", true);
				filters[2] = new MetaSearchInfo(MetaValueTypes.DateTime, "Publication_Date", DateTime.Now, SearchCondition.LessOrEqual);
				var newsList = mgr.Content.GetContent("Publication_Date DESC", filters);

				// only retrieve published items
				foreach (IContent item in newsList)
				{
					var news = new NewsDetails(item);
					if (item.Status == ContentStatus.Published) published.Add(news);
				}
				
				// write to cache
				cache = JsonConvert.SerializeObject(published);
				CacheHelper.WriteFile(file.FullName, cache);
			}
			else
				// Cache is still valid
				published = JsonConvert.DeserializeObject<NewsList>(cache);

			return published;
		}

		#endregion
	}
}